
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <title>NES游戏开发</title>
    <link rel="stylesheet" href="index.css"/>
</head>

<body>

<div class="main">
<p><h1>10、游戏循环</h1>
</p><p>我们来谈谈游戏循环
</p><p>游戏需要循环，while(1){}
</p><p>
</p><p>循环里的第一项是ppu_wait_nmi()，他会等待新一帧的开始触发nmi，每秒60帧（欧版是50帧）
</p><p>但是，如果游戏的逻辑太过复杂将会耗费更多的时间，并且超过了nmi触发的开始，那么等到了ppu_wait_nmi()之后，他会等待下一帧，这样会让游戏变得很慢。
</p><p>好在现在还没发生过这种情况，因为我们的代码太短了，但是后面做的游戏中就不那么简单了，所以还是要注意代码的简洁。
</p><p>
</p><p>控制器使用了
</p><p>pad_poll(0)
</p><p>get_pad_new(0)
</p><p>
</p><p>然后要说的是clear_vram_buffer()，这些代码对我来说是对一无二的。由于nmi的作用，我们需要重写他，以便让他能实现更多功能
</p><p>score_lives_draw() 使用one_vram_buffer() 重新显示屏幕顶端的计分板。
</p><p>这些类似vram_put()，但他们将被放在缓冲区中，并且nmi代码将在v-blank期间自动将它们推送到ppu。
</p><p>
</p><p>
</p><p>然后是游戏的逻辑，移动球拍，移动球，检查是否有碰撞。
</p><p>移动手柄
</p><p><pre>
</p><p>if(pad1 & PAD_LEFT){
</p><p>    Paddle.X -= 2;
</p><p>    if(Paddle.X < PADDLE_MIN) Paddle.X = PADDLE_MIN;
</p><p>}
</p><p>if(pad1 & PAD_RIGHT){
</p><p>    Paddle.X += 2;
</p><p>    if(Paddle.X > PADDLE_MAX) Paddle.X = PADDLE_MAX;
</p><p>}
</p><p></pre>
</p><p>
</p><p>移动球，如果他是移动的
</p><p><pre>
</p><p>if(ball_direction == GOING_UP){
</p><p>    Ball.Y -= 3;
</p><p>    if(Ball.Y < MAX_UP){
</p><p>	ball_direction = GOING_DOWN;
</p><p>    }
</p><p>}
</p><p>else { // going down
</p><p>    Ball.Y += 3;
</p><p>    if(Ball.Y > MAX_DOWN){
</p><p>	--lives01;
</p><p>	ball_state = BALL_OFF;
</p><p>    }
</p><p>}
</p><p></pre>
</p><p>
</p><p>然后到了绘制精灵，首先先要清除旧的，然后重新绘制所有活动的精灵。
</p><p>
</p><p>如果一个块被球命中，hit_block()，从碰撞图中删除这个块，用一个空白块填充这个区域。
</p><p>同样，我使用vram缓冲区暂时存储它，并在v-blank期间自动将其发送到PPU。
</p><p>
</p><p>通常情况下，我会使用很多个 
</p><p>game_states：比如标题，游戏，暂停，结束。
</p><p>我使用不同的 ball_states：飞出屏幕BALL_OFF，准备发射球BALL_STUCK (卡在平板边缘)，球在移动BALL_ACTIVE
</p><p>
</p><p>我使用NES Screen Tool制作背景，在c1.csv中定义了可以破坏的图块。我没用Tiled工具，因为他显示容易，但是如果有修改对应的可破坏的图块布局也需要跟着修改（也就是前面的c1.csv）
</p><p>
</p><p><pre>
</p><p>const unsigned char c1 [] = {
</p><p>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0，
</p><p>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0，
</p><p>0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0，
</p><p>0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0，
</p><p>0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0，
</p><p>0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0，
</p><p>0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0，
</p><p>0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0，
</p><p>
</p><p>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0，
</p><p>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0，
</p><p>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0，
</p><p>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0，
</p><p>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0，
</p><p>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0，
</p><p>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0，
</p><p>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</p><p>};
</p><p></pre>
</p><p>
</p><p>使用灰色的砖块在NES Screen Tool中画图，然后压缩导出RLE文件。
</p><p>
</p><p><img src="img/10_1.png">
</p><p>
</p><p>我在NES Screen Tool中预先更改了属性表，然后保存背景。这就是瓷砖的颜色，它们是不同的调色板。但他们都使用相同的瓷砖。这是属性检查器视图（按“A”）。
</p><p>
</p><p><img src="img/10_2.png">
</p><p>
</p><p>每帧都会更新记分牌，请注意我将得分的每个数字都保存为单独的变量（等级为0-9）
</p><p>score10是10位数，score01是个位数。由于6502处理器缺少内置数学运算，因此除法和模数对NES的操作非常慢，因此保持每个数字分开可加快代码速度。
</p><p>
</p><p><img src="img/10_3.png">
</p><p>
</p><p>https://github.com/nesdoug/12_Break
</p><p>
</p><p>现在把这个变成游戏已经很简单了，但是横向移动需要写很复杂的代码，我会把示例写的尽可能简单。
</p><p>
</p><p>
</p><p>
</p><p>
</p><p>
</p><p>
</p><p>
</p><p>
</p></div>
<script>
var h1 = document.getElementsByTagName("h1");
if(h1.length>0){
    document.title = h1[0].innerHTML;
}
</script>
</body>
</html>
